﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ExpressionEvaluator;
using System.Linq.Expressions;

namespace ExpressionEvalTest
{
	/// <summary>
	/// Summary description for UnitTest1
	/// </summary>
	[TestClass]
	public class ParserTest
	{

		#region Additional test attributes
		//
		// You can use the following additional attributes as you write your tests:
		//
		// Use ClassInitialize to run code before running the first test in the class
		// [ClassInitialize()]
		// public static void MyClassInitialize(TestContext testContext) { }
		//
		// Use ClassCleanup to run code after all tests in a class have run
		// [ClassCleanup()]
		// public static void MyClassCleanup() { }
		//
		// Use TestInitialize to run code before running each test 
		// [TestInitialize()]
		// public void MyTestInitialize() { }
		//
		// Use TestCleanup to run code after each test has run
		// [TestCleanup()]
		// public void MyTestCleanup() { }
		//
		#endregion

		[TestMethod]
		public void Basic()
		{
			CodeBlockCollection result = EvalParser.ParseCode("op1&&op2||(op3.function(\"te\\\"xt\") + op4.function2(\"text2\"))");
			Assert.IsNotNull(result);
		}

		[TestMethod]
		[ExpectedException(typeof(CodeParserException))]
		public void EmptyFails()
		{
			CodeBlockCollection result = EvalParser.ParseCode("");
		}

		[TestMethod]
		[ExpectedException(typeof(CodeParserException))]
		public void OnlyWhiteCharactersFails()
		{
			CodeBlockCollection result = EvalParser.ParseCode(" \n ");
		}

		[TestMethod]
		public void IgnoreWhiteSpaceInCode()
		{
			Assert.AreEqual(
				EvalParser.ParseCode("op1&&op2").ToString(),
				EvalParser.ParseCode("op1\n&&\n op2 ").ToString());
		}

		[TestMethod]
		public void TestQoutesInText()
		{
			CodeBlockCollection result = EvalParser.ParseCode(@"""text with \"" quotes""");
			Assert.AreEqual("text with \" quotes", result[0].Text);
		}

		[TestMethod]
		public void TestEmptyText()
		{
			CodeBlockCollection result = EvalParser.ParseCode(@"""""");
			Assert.AreEqual("", result[0].Text);
		}
		[TestMethod]
		public void TestEndingWithBackSlash()
		{
			CodeBlockCollection result = EvalParser.ParseCode(@"""\\\\test\\\\test\\\\""==""""");
			Assert.AreEqual(@"\\test\\test\\", result[0].Text);
		}
		[TestMethod]
		public void TestMinusSign()
		{
			CodeBlockCollection result = EvalParser.ParseCode(@"1=-1");
			Assert.AreEqual(4, result.Count);
			result = EvalParser.ParseCode(@"-1=-1");
			Assert.AreEqual(5, result.Count);
			result = EvalParser.ParseCode(@"1-1");
			Assert.AreEqual(3, result.Count);
		}
		[TestMethod]
		public void TestEndingWithBackSlash2()
		{
			CodeBlockCollection result = EvalParser.ParseCode(@"(item.Path == ""\\\\chsa3017\\D$\\data\\groups\\KF\\KF94\\VIPServices\\"")&&(item.FileName == ""backup Kontakte.pst"")");
			Assert.AreEqual(15, result.Count);
		}
		[TestMethod]
		public void TestQoutesAfterOperator()
		{
			CodeBlockCollection result = EvalParser.ParseCode(@"op.property==""text""");
			Assert.AreEqual(CodeTypeEnum.Executable, result[0].CodeType);
			Assert.AreEqual(CodeTypeEnum.Delimiter, result[1].CodeType);
			Assert.AreEqual(CodeTypeEnum.Executable, result[2].CodeType);
			Assert.AreEqual(CodeTypeEnum.Operation, result[3].CodeType);
			Assert.AreEqual(CodeTypeEnum.Text, result[4].CodeType);
		}

		[TestMethod]
		public void SimpleOperationWithTwoExecutables()
		{
			CodeBlockCollection result = EvalParser.ParseCode(@"op1&&op2");
			Assert.AreEqual(3, result.Count);
			Assert.AreEqual(DummyCodeBlock("op1", CodeTypeEnum.Executable), result[0]);
			Assert.AreEqual(DummyCodeBlock("&&", CodeTypeEnum.Operation), result[1]);
			Assert.AreEqual(DummyCodeBlock("op2", CodeTypeEnum.Executable), result[2]);
		}

		[TestMethod]
		public void ObjectFunctionWithTwoParameters()
		{
			CodeBlockCollection result = EvalParser.ParseCode(@"object.function(par1,""par2"")");
			Assert.AreEqual(8, result.Count);
			Assert.AreEqual(DummyCodeBlock("object", CodeTypeEnum.Executable), result[0]);
			Assert.AreEqual(DummyCodeBlock(".", CodeTypeEnum.Delimiter), result[1]);
			Assert.AreEqual(DummyCodeBlock("function", CodeTypeEnum.Executable), result[2]);
			Assert.AreEqual(DummyCodeBlock("(", CodeTypeEnum.Delimiter), result[3]);
			Assert.AreEqual(DummyCodeBlock("par1", CodeTypeEnum.Executable), result[4]);
			Assert.AreEqual(DummyCodeBlock(",", CodeTypeEnum.Delimiter), result[5]);
			Assert.AreEqual(DummyCodeBlock("par2", CodeTypeEnum.Text), result[6]);
			Assert.AreEqual(DummyCodeBlock(")", CodeTypeEnum.Delimiter), result[7]);
		}

		[TestMethod]
		public void OperationsDelimitedByBracket()
		{
			CodeBlockCollection result = EvalParser.ParseCode(@"op1&&(op2&&(op3||op4)&&op5)");
			Assert.AreEqual(13, result.Count);
			Assert.AreEqual(DummyCodeBlock("op1", CodeTypeEnum.Executable), result[0]);
			Assert.AreEqual(DummyCodeBlock("&&", CodeTypeEnum.Operation), result[1]);
			Assert.AreEqual(DummyCodeBlock("(", CodeTypeEnum.Delimiter), result[2]);
			Assert.AreEqual(DummyCodeBlock("op2", CodeTypeEnum.Executable), result[3]);
			Assert.AreEqual(DummyCodeBlock("&&", CodeTypeEnum.Operation), result[4]);
			Assert.AreEqual(DummyCodeBlock("(", CodeTypeEnum.Delimiter), result[5]);
			Assert.AreEqual(DummyCodeBlock("op3", CodeTypeEnum.Executable), result[6]);
			Assert.AreEqual(DummyCodeBlock("||", CodeTypeEnum.Operation), result[7]);
			Assert.AreEqual(DummyCodeBlock("op4", CodeTypeEnum.Executable), result[8]);
			Assert.AreEqual(DummyCodeBlock(")", CodeTypeEnum.Delimiter), result[9]);
			Assert.AreEqual(DummyCodeBlock("&&", CodeTypeEnum.Operation), result[10]);
			Assert.AreEqual(DummyCodeBlock("op5", CodeTypeEnum.Executable), result[11]);
			Assert.AreEqual(DummyCodeBlock(")", CodeTypeEnum.Delimiter), result[12]);
		}

        [TestMethod]
        public void AnonymousTypesSupport()
        {
            // serialized anonymous type
            CodeBlockCollection result = EvalParser.ParseCode(@"new <>f__AnonymousType0`2(Text = item.ToString(), Length = item.Length )");
            Assert.AreEqual(DummyCodeBlock("<>f__AnonymousType0`2", CodeTypeEnum.Executable), result[1]);

            // direct anonymous type
            result = EvalParser.ParseCode(@"new { Text = item.ToString(), Length = item.Length }");
            Assert.AreEqual(DummyCodeBlock("{", CodeTypeEnum.Delimiter), result[1]);
            Assert.AreEqual(DummyCodeBlock("}", CodeTypeEnum.Delimiter), result[result.Count - 1]);
        }
		/// <summary>
		/// Crates dummy the code block.
		/// </summary>
		/// <param name="text">The text.</param>
		/// <param name="codeType">Type of the code.</param>
		/// <returns>Dummy codeblock for comparision.</returns>
		private static CodeBlock DummyCodeBlock(string text, CodeTypeEnum codeType)
		{
			return new CodeBlock(text, codeType, 0, 0);
		}
	}
}
